Skip to content

feat: type annotations 📚#131

Open
timfennis wants to merge 14 commits intomasterfrom
feature/type-annotations
Open

feat: type annotations 📚#131
timfennis wants to merge 14 commits intomasterfrom
feature/type-annotations

Conversation

@timfennis
Copy link
Copy Markdown
Owner

@timfennis timfennis commented Apr 18, 2026

This PR adds support for type annotations in a few positions.

let a: Int = 3;
fn bar(b: Int) -> Int { b + 1 }

It notably does not support:

  • Generic type annotations
  • Type annotations in for loops
for a, b: (Int, Int) in test {

}

@timfennis timfennis force-pushed the feature/type-annotations branch from c2ca703 to 234f27a Compare May 7, 2026 08:03
timfennis and others added 13 commits May 7, 2026 10:15
Co-authored-by: Claude <noreply@anthropic.com>
The lexer greedily tokenises `>>`, `>=`, and `>>=` as single tokens,
which breaks nested generic type annotations like `List<List<Int>>`.
The parser now splits these compound tokens when closing angle brackets
in type parameter lists.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduces TypeBinding enum (Inferred/Annotated) to track whether a
variable was declared with an explicit type annotation. Annotated
bindings refuse type widening on reassignment, emitting a type mismatch
error instead.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Extends the annotated binding check to OpAssignment (+=, /=, etc.).
Also fixes a bug where destructured `let v, n = 100, 100` incorrectly
marked sub-bindings as annotated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Adds `named_parameter` parser for function param lists, separate from
`named_binding` (used by `let` destructuring). Params now accept
optional type annotations (e.g. `fn foo(x: Int)`). Also adds
`TypeSignature::from_annotated_bindings` constructor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Parse `-> Type` return type annotations on function declarations
- Analyser validates inferred return type against annotation
- Parameter type annotations now feed into analysis (no longer ignored)
- Register Int-specific overloads for +, -, *, % with fast i64 path
- Register Float-specific overloads for +, -, *, /, %
- Widen container element type on index op-assignment (e.g. x[0] /= 3)
- Add StaticType::with_element_type helper

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…holders 🔄

When pre-registering a recursive function, use the declared return type
annotation instead of Any so that recursive calls resolve correctly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Introduce FunctionParameter to unify parameter representation in the
AST, replacing TypeSignature on FunctionDeclaration. Move inferred
return types to an AnalysisResult side table so the LSP can distinguish
annotated vs inferred return types. Walk parameter lvalues in the
visitor so parameter type hints are emitted.

Also compute the LUB of return types across all overload candidates in
dynamic bindings, improving type inference for overloaded functions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@timfennis timfennis force-pushed the feature/type-annotations branch from 234f27a to 8b928a8 Compare May 7, 2026 08:17
@timfennis timfennis marked this pull request as ready for review May 7, 2026 08:25
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8b928a8510

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 781 to 783
if elems.len() != seq.len() {
Box::new(std::iter::repeat(&StaticType::Any))
} else {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Reject annotated tuple destructures with arity mismatch

When an annotated destructuring pattern has a different arity than its tuple annotation, this branch replaces all expected element types with Any instead of reporting a mismatch. That means code like let (a, b): (Int) = (1, 2); is accepted and both bindings become effectively unconstrained, so the declared annotation is silently ignored rather than enforced. This is a correctness regression in the new annotation feature and should emit a type error when tuple arities differ.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@codex let me know if you approve of my fix

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9794545e4d

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +802 to +803
for (sub_lvalue, sub_type, found_type) in
izip!(seq.iter_mut(), sub_types, found_types)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Bind remaining lvalues when RHS tuple is shorter

Using izip!(seq.iter_mut(), sub_types, found_types) stops iteration at the shortest iterator, so in annotated destructuring like let (a, b, c): (Int, Int, Int) = (1, 2); the third binding is never created after the type-mismatch error is emitted. That causes follow-on "identifier not previously declared" errors for c, which is a regression in analyzer recovery behavior; this loop should still declare all declared lvalues (e.g., defaulting missing found types to Any) so analysis can continue consistently.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant